package com.zhangyu;

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.stack.core.Identifiers;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.*;
import redis.clients.jedis.Jedis;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
import static org.eclipse.milo.opcua.stack.core.util.ConversionUtil.l;

public class MyOpcUaClient {

    private OpcUaClient opcUaClient;
    OpcUtil opcUtil = new OpcUtil();

    private UaSubscription subscription;
    List<UaMonitoredItem> monitoredItems;

    Timer timer;

    public void createClient() {
        try {
            opcUaClient = opcUtil.createClient("opc.tcp://10.168.6.44:53530/OPCUA/SimulationServer", SecurityPolicy.None);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void connect() {
        try {
            opcUaClient.connect().get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    public void browseNodes() {
        opcUtil.browseNode("", opcUaClient, Identifiers.RootFolder, 0);
    }

    public void subscriptionNode() throws Exception {
        this.subscription = this.opcUaClient.getSubscriptionManager().createSubscription(500.0).get();
        UInteger clientHandle = this.subscription.nextClientHandle();
        MonitoringParameters parameters = new MonitoringParameters(clientHandle, 500.0, // sampling interval
                null, // filter, null means use default
                uint(10), // queue size
                true // discard oldest
        );
        BiConsumer<UaMonitoredItem, Integer> onItemCreated = (item, id) -> item
                .setValueConsumer(this::onSubscriptionValue);
        this.monitoredItems = this.subscription
                .createMonitoredItems(TimestampsToReturn.Both, GHLZMonitoredNode.getGHLZMonitoredNode(parameters), onItemCreated).get();
        for (UaMonitoredItem item : this.monitoredItems) {
            if (item.getStatusCode().isGood()) {
                System.out.println("item created for nodeId=" + item.getReadValueId().getNodeId());
            } else {
                System.out.println("failed to create item for nodeId=" + item.getReadValueId().getNodeId() + " (status="
                        + item.getStatusCode());
            }
        }
    }

    public void unsubscribe() {
        this.subscription.deleteMonitoredItems(this.monitoredItems);
    }

    private void onSubscriptionValue(UaMonitoredItem item, DataValue value) {
        System.out.println("subscription value received: item=" + item.getReadValueId().getNodeId() + ", value="
                + value.getValue());
    }

    public void historyRead() throws Exception {
        HistoryReadDetails historyReadDetails = new ReadRawModifiedDetails(false, DateTime.MIN_VALUE, DateTime.now(),
                uint(0), true);
        HistoryReadValueId historyReadValueId = new HistoryReadValueId(new NodeId(5, "Counter1"), null,
                QualifiedName.NULL_VALUE, ByteString.NULL_VALUE);
        List<HistoryReadValueId> nodesToRead = new ArrayList<>();
        nodesToRead.add(historyReadValueId);

        HistoryReadResponse historyReadResponse = this.opcUaClient
                .historyRead(historyReadDetails, TimestampsToReturn.Both, false, nodesToRead).get();
        HistoryReadResult[] historyReadResults = historyReadResponse.getResults();
        if (historyReadResults != null) {
            HistoryReadResult historyReadResult = historyReadResults[0];
            HistoryData historyData = (HistoryData) historyReadResult.getHistoryData()
                    .decode(this.opcUaClient.getSerializationContext());

            List<DataValue> dataValues = l(historyData.getDataValues());

            dataValues.forEach(v -> System.out.println("value=" + v));
        }
    }

    public void startCollect() {
        Jedis jedis = new Jedis("localhost");
        jedis.flushAll();

        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                try {
                    ReadResponse response = opcUaClient.read(0.0, TimestampsToReturn.Both, GHLZNode.getGHLZNode()).get();
                    Calendar c = Calendar.getInstance();
                    int hour = c.get(Calendar.HOUR_OF_DAY);
                    jedis.rpush("Counter1:" + hour, response.getResults()[0].getValue().getValue().toString());
                    jedis.rpush("Random1:" + hour, response.getResults()[1].getValue().getValue().toString());
                    jedis.rpush("Sinusoid1:" + hour, response.getResults()[2].getValue().getValue().toString());
                    jedis.rpush("Square1:" + hour, response.getResults()[3].getValue().getValue().toString());
                    jedis.rpush("Sawtooth1:" + hour, response.getResults()[4].getValue().getValue().toString());
                    jedis.rpush("Triangle1:" + hour, response.getResults()[5].getValue().getValue().toString());
                    jedis.rpush("Expression1:" + hour, response.getResults()[6].getValue().getValue().toString());
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }, 0, 1000);
    }

    public void stopCollect() {
        timer.cancel();
    }
}
